diff --git a/static/js/instance_detail.js b/static/js/instance_detail.js new file mode 100644 index 0000000..fe62eaf --- /dev/null +++ b/static/js/instance_detail.js @@ -0,0 +1,1138 @@ +// Basic test to verify script execution +console.log('Script block loaded'); + +// Test function to verify DOM elements exist +function testElements() { + console.log('Testing DOM elements...'); + const elements = [ + 'company-name-value', + 'company-industry', + 'company-description', + 'company-email', + 'company-phone', + 'company-website', + 'company-address' + ]; + + elements.forEach(id => { + const element = document.getElementById(id); + console.log(`Element ${id} exists:`, !!element); + }); +} + +let statusUpdateInterval; + +// Function to check instance status +async function checkInstanceStatus() { + console.log('checkInstanceStatus called'); + try { + const response = await fetch(`/instances/{{ instance.id }}/status`); + if (!response.ok) throw new Error('Failed to check instance status'); + + const data = await response.json(); + console.log('Status data:', data); + + // Update activity status + const activityIcon = document.querySelector('.card:first-child .fa-circle-notch'); + const activityBadge = document.querySelector('.card:first-child .badge'); + const activityDetails = document.querySelector('.card:first-child .text-muted'); + + if (activityIcon) { + activityIcon.className = `fas fa-circle-notch fa-spin ${data.status === 'active' ? 'text-success' : 'text-danger'} fa-2x`; + } + if (activityBadge) { + activityBadge.className = `badge bg-${data.status === 'active' ? 'success' : 'danger'}`; + activityBadge.textContent = data.status.charAt(0).toUpperCase() + data.status.slice(1); + } + if (activityDetails) { + // Only update if we have status details + if (data.status_details) { + activityDetails.textContent = data.status_details; + activityDetails.style.display = 'block'; + } else { + activityDetails.style.display = 'none'; + } + } + } catch (error) { + console.error('Error checking instance status:', error); + } +} + +// Function to check authentication status +async function checkAuthStatus() { + console.log('checkAuthStatus called'); + try { + const response = await fetch(`/instances/{{ instance.id }}/auth-status`); + if (!response.ok) throw new Error('Failed to check authentication status'); + + const data = await response.json(); + console.log('Auth data:', data); + + // Update authentication status + const authIcon = document.querySelector('.card:nth-child(2) .fa-shield-alt'); + const authBadge = document.querySelector('.card:nth-child(2) .badge'); + const authDetails = document.querySelector('.card:nth-child(2) .text-muted'); + + if (authIcon) { + authIcon.className = `fas fa-shield-alt ${data.authenticated ? 'text-success' : 'text-warning'} fa-2x`; + } + if (authBadge) { + authBadge.className = `badge bg-${data.authenticated ? 'success' : 'warning'}`; + authBadge.textContent = data.authenticated ? 'Authenticated' : 'Not Authenticated'; + } + if (authDetails) { + authDetails.textContent = data.authenticated ? '' : 'This instance needs to be authenticated'; + } + } catch (error) { + console.error('Error checking authentication status:', error); + } +} + +// Function to fetch company information +async function fetchCompanyInfo() { + console.log('fetchCompanyInfo called'); + try { + // First get JWT token + console.log('Getting management token...'); + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + console.log('Token response status:', tokenResponse.status); + if (!tokenResponse.ok) { + console.error('Failed to get management token:', tokenResponse.status); + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + console.log('Token data received:', tokenData); + + if (!tokenData.token) { + console.error('No token in response'); + throw new Error('No token received'); + } + + // Then fetch settings using the JWT token + console.log('Fetching settings from:', `{{ instance.main_url }}/api/admin/settings`); + const response = await fetch(`{{ instance.main_url }}/api/admin/settings`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + console.log('Settings response status:', response.status); + if (!response.ok) { + console.error('Settings fetch failed:', response.status); + throw new Error('Failed to fetch company information'); + } + + const data = await response.json(); + console.log('Settings data received:', data); + + // Update company name separately with debugging + const companyNameElement = document.getElementById('company-name-value'); + const companyNameLabel = document.getElementById('company-name-label'); + + // Ensure label text is set and maintained + if (companyNameLabel) { + companyNameLabel.textContent = 'Company Name:'; + } + + if (companyNameElement) { + companyNameElement.textContent = data.company_name || 'Not set'; + } + + // Update other company details + const updateElement = (id, value) => { + const element = document.getElementById(id); + if (element && element.classList.contains('company-value')) { + element.textContent = value || 'Not set'; + } + }; + + // Update other company details + updateElement('company-industry', data.company_industry); + updateElement('company-description', data.company_description); + + // Update contact information + const emailElement = document.getElementById('company-email'); + const phoneElement = document.getElementById('company-phone'); + const websiteElement = document.getElementById('company-website'); + + // Update email with mailto link + if (emailElement && emailElement.classList.contains('company-value')) { + if (data.company_email && data.company_email !== 'Not set') { + emailElement.innerHTML = ` + ${data.company_email} + `; + } else { + emailElement.textContent = 'Not set'; + } + } + + // Update phone with tel link + if (phoneElement && phoneElement.classList.contains('company-value')) { + if (data.company_phone && data.company_phone !== 'Not set') { + phoneElement.innerHTML = ` + ${data.company_phone} + `; + } else { + phoneElement.textContent = 'Not set'; + } + } + + // Update website with external link + if (websiteElement && websiteElement.classList.contains('company-value')) { + if (data.company_website && data.company_website !== 'Not set') { + // Ensure website has http/https prefix + let websiteUrl = data.company_website; + if (!websiteUrl.startsWith('http://') && !websiteUrl.startsWith('https://')) { + websiteUrl = 'https://' + websiteUrl; + } + websiteElement.innerHTML = ` + ${data.company_website} + `; + } else { + websiteElement.textContent = 'Not set'; + } + } + + // Format address + const addressParts = [ + data.company_address, + data.company_city, + data.company_state, + data.company_zip, + data.company_country + ].filter(Boolean); + updateElement('company-address', addressParts.length ? addressParts.join(', ') : 'Not set'); + + console.log('Company info update complete'); + } catch (error) { + console.error('Error in fetchCompanyInfo:', error); + // Show error state in all fields + const fields = ['company-name-value', 'company-industry', 'company-description', + 'company-email', 'company-phone', 'company-website', 'company-address']; + fields.forEach(field => { + const element = document.getElementById(field); + if (element && element.classList.contains('company-value')) { + element.textContent = 'Error loading data'; + } + }); + } +} + +// Function to update all statuses +async function updateAllStatuses() { + console.log('Starting updateAllStatuses...'); + try { + await Promise.all([ + checkInstanceStatus(), + checkAuthStatus(), + fetchCompanyInfo() + ]); + console.log('All statuses updated successfully'); + } catch (error) { + console.error('Error in updateAllStatuses:', error); + } +} + +// Initialize status updates +document.addEventListener('DOMContentLoaded', function() { + console.log('DOM loaded, initializing updates...'); + testElements(); // Test if elements exist + // Initial update + updateAllStatuses(); + + // Set up periodic updates (every 10 minutes) + statusUpdateInterval = setInterval(updateAllStatuses, 600000); + console.log('Periodic updates initialized'); +}); + +// Clean up interval when page is unloaded +window.addEventListener('beforeunload', function() { + if (statusUpdateInterval) { + clearInterval(statusUpdateInterval); + } +}); + +// Contacts Management Functions +async function fetchContacts() { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/contacts`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch contacts'); + } + + const contacts = await response.json(); + const contactsList = document.getElementById('contactsTableBody'); + contactsList.innerHTML = ''; + + contacts.forEach(contact => { + const row = document.createElement('tr'); + + // Role badge color mapping + const roleBadgeClass = { + 'admin': 'bg-danger', + 'manager': 'bg-warning', + 'user': 'bg-info' + }[contact.role] || 'bg-secondary'; + + // Status badge color mapping + const statusBadgeClass = contact.is_active ? 'bg-success' : 'bg-secondary'; + + // Format phone number for tel: link (remove any non-digit characters) + const phoneNumber = contact.phone ? contact.phone.replace(/\D/g, '') : ''; + + row.innerHTML = ` + ${contact.username} ${contact.last_name} + + + + ${contact.email} + + + + + ${contact.phone ? ` + + + ${contact.phone} + + + ` : '-'} + + ${contact.company || '-'} + ${contact.position || '-'} + ${contact.role.charAt(0).toUpperCase() + contact.role.slice(1)} + ${contact.is_active ? 'Active' : 'Inactive'} + +
+ + +
+ + `; + contactsList.appendChild(row); + }); + } catch (error) { + console.error('Error fetching contacts:', error); + showToast('Error loading contacts', 'error'); + } +} + +async function addContact(event) { + event.preventDefault(); + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const formData = new FormData(event.target); + + const response = await fetch(`{{ instance.main_url }}/api/admin/contacts`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + }, + body: JSON.stringify({ + username: formData.get('name'), + email: formData.get('email'), + phone: formData.get('phone'), + company: formData.get('company'), + position: formData.get('position'), + role: formData.get('role'), + is_active: formData.get('status') === 'active' + }) + }); + + if (!response.ok) { + throw new Error('Failed to add contact'); + } + + // Close modal and refresh list + const modal = bootstrap.Modal.getInstance(document.getElementById('addContactModal')); + modal.hide(); + fetchContacts(); + + // Show success message + showToast('Contact added successfully', 'success'); + } catch (error) { + console.error('Error adding contact:', error); + showToast('Error adding contact', 'error'); + } +} + +async function editContact(id) { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/contacts/${id}`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch contact details'); + } + + const contact = await response.json(); + + // Populate form + document.getElementById('edit-contact-id').value = contact.id; + document.getElementById('edit-name').value = contact.username; + document.getElementById('edit-email').value = contact.email || ''; + document.getElementById('edit-phone').value = contact.phone || ''; + document.getElementById('edit-company').value = contact.company || ''; + document.getElementById('edit-position').value = contact.position || ''; + document.getElementById('edit-role').value = contact.role; + document.getElementById('edit-status').value = contact.is_active ? 'active' : 'inactive'; + + // Show modal + const modal = new bootstrap.Modal(document.getElementById('editContactModal')); + modal.show(); + } catch (error) { + console.error('Error fetching contact details:', error); + showToast('Error loading contact details', 'error'); + } +} + +async function updateContact(event) { + event.preventDefault(); + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const formData = new FormData(event.target); + const id = formData.get('id'); + + const response = await fetch(`{{ instance.main_url }}/api/admin/contacts/${id}`, { + method: 'PUT', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + }, + body: JSON.stringify({ + username: formData.get('name'), + email: formData.get('email'), + phone: formData.get('phone'), + company: formData.get('company'), + position: formData.get('position'), + role: formData.get('role'), + is_active: formData.get('status') === 'active' + }) + }); + + if (!response.ok) { + throw new Error('Failed to update contact'); + } + + // Close modal and refresh list + const modal = bootstrap.Modal.getInstance(document.getElementById('editContactModal')); + modal.hide(); + fetchContacts(); + + // Show success message + showToast('Contact updated successfully', 'success'); + } catch (error) { + console.error('Error updating contact:', error); + showToast('Error updating contact', 'error'); + } +} + +async function deleteContact(id) { + // Store the contact ID for the delete confirmation + const deleteModal = new bootstrap.Modal(document.getElementById('deleteContactModal')); + document.getElementById('confirmDelete').onclick = async () => { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/contacts/${id}`, { + method: 'DELETE', + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to delete contact'); + } + + // Close modal and refresh list + deleteModal.hide(); + fetchContacts(); + + // Show success message + showToast('Contact deleted successfully', 'success'); + } catch (error) { + console.error('Error deleting contact:', error); + showToast('Error deleting contact', 'error'); + } + }; + + // Show the delete confirmation modal + deleteModal.show(); +} + +// Helper function to show toast messages +function showToast(title, message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `toast align-items-center text-white bg-${type} border-0`; + toast.setAttribute('role', 'alert'); + toast.setAttribute('aria-live', 'assertive'); + toast.setAttribute('aria-atomic', 'true'); + + toast.innerHTML = ` +
+
+ ${title}
+ ${message} +
+ +
+ `; + + const toastContainer = document.getElementById('toastContainer') || (() => { + const container = document.createElement('div'); + container.id = 'toastContainer'; + container.className = 'toast-container position-fixed bottom-0 end-0 p-3'; + document.body.appendChild(container); + return container; + })(); + + toastContainer.appendChild(toast); + const bsToast = new bootstrap.Toast(toast); + bsToast.show(); + + toast.addEventListener('hidden.bs.toast', () => { + toast.remove(); + }); +} + +// Event Listeners +document.addEventListener('DOMContentLoaded', function() { + // ... existing event listeners ... + + // Fetch contacts when contacts tab is shown + document.getElementById('contacts-tab').addEventListener('shown.bs.tab', fetchContacts); + + // Fetch logs when logs tab is shown + document.getElementById('logs-tab').addEventListener('shown.bs.tab', fetchLogs); + + // Fetch mails when mails tab is shown + document.getElementById('mails-tab').addEventListener('shown.bs.tab', fetchMails); + + // Debug the company name label + const label = document.getElementById('company-name-label'); + if (label) { + console.log('Company name label found:', label); + + // Create a MutationObserver to watch for changes + const observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + console.log('Label changed:', mutation); + console.log('Old value:', mutation.oldValue); + console.log('New value:', label.textContent); + }); + }); + + // Start observing + observer.observe(label, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } else { + console.log('Company name label not found'); + } +}); + +// Function to fetch logs +async function fetchLogs() { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/logs`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch logs'); + } + + const data = await response.json(); + const logsTableBody = document.getElementById('logsTableBody'); + logsTableBody.innerHTML = ''; + + // Fetch all users to map IDs to names + const usersResponse = await fetch(`{{ instance.main_url }}/api/admin/contacts`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!usersResponse.ok) { + throw new Error('Failed to fetch users'); + } + + const usersData = await usersResponse.json(); + const userMap = new Map(usersData.map(user => [user.id, `${user.username} ${user.last_name}`])); + + data.events.forEach(event => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${new Date(event.timestamp).toLocaleString()} + ${event.event_type || '-'} + ${event.user_id ? userMap.get(event.user_id) || `User ${event.user_id}` : '-'} + ${event.ip_address || '-'} + `; + logsTableBody.appendChild(row); + }); + + // Update pagination + updateLogsPagination(data.current_page, data.pages); + } catch (error) { + console.error('Error fetching logs:', error); + showToast('Error loading logs', 'error'); + } +} + +// Function to fetch mails +async function fetchMails() { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/mail-logs`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch mail logs'); + } + + const data = await response.json(); + const mailsTableBody = document.querySelector('#mails .table tbody'); + mailsTableBody.innerHTML = ''; + + data.mails.forEach(mail => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${new Date(mail.created_at).toLocaleString()} + ${mail.recipient} + ${mail.subject} + ${mail.sent_at ? new Date(mail.sent_at).toLocaleString() : '-'} + `; + mailsTableBody.appendChild(row); + }); + + // Update pagination + updateMailsPagination(data.current_page, data.pages); + } catch (error) { + console.error('Error fetching mail logs:', error); + showToast('Error loading mail logs', 'error'); + } +} + +// Helper function to get mail status badge class +function getMailStatusBadgeClass(status) { + const classes = { + 'pending': 'warning', + 'sent': 'success', + 'failed': 'danger' + }; + return classes[status] || 'secondary'; +} + +// Function to update logs pagination +function updateLogsPagination(currentPage, totalPages) { + const pagination = document.getElementById('logsPagination'); + if (!pagination) return; + + pagination.innerHTML = ''; + + // Previous button + const prevLi = document.createElement('li'); + prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`; + prevLi.innerHTML = ` + + `; + pagination.appendChild(prevLi); + + // Page numbers + for (let i = 1; i <= totalPages; i++) { + const li = document.createElement('li'); + li.className = `page-item ${i === currentPage ? 'active' : ''}`; + li.innerHTML = ` + + `; + pagination.appendChild(li); + } + + // Next button + const nextLi = document.createElement('li'); + nextLi.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`; + nextLi.innerHTML = ` + + `; + pagination.appendChild(nextLi); +} + +// Function to update mails pagination +function updateMailsPagination(currentPage, totalPages) { + const pagination = document.querySelector('#mails .pagination'); + if (!pagination) return; + + pagination.innerHTML = ''; + + // Previous button + const prevLi = document.createElement('li'); + prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`; + prevLi.innerHTML = ` + + `; + pagination.appendChild(prevLi); + + // Page numbers + for (let i = 1; i <= totalPages; i++) { + const li = document.createElement('li'); + li.className = `page-item ${i === currentPage ? 'active' : ''}`; + li.innerHTML = ` + + `; + pagination.appendChild(li); + } + + // Next button + const nextLi = document.createElement('li'); + nextLi.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`; + nextLi.innerHTML = ` + + `; + pagination.appendChild(nextLi); +} + +// Function to change logs page +async function changeLogsPage(page) { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/logs?page=${page}`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch logs'); + } + + const data = await response.json(); + + // Fetch all users to map IDs to names + const usersResponse = await fetch(`{{ instance.main_url }}/api/admin/contacts`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!usersResponse.ok) { + throw new Error('Failed to fetch users'); + } + + const usersData = await usersResponse.json(); + const userMap = new Map(usersData.map(user => [user.id, `${user.username} ${user.last_name}`])); + + const logsTableBody = document.getElementById('logsTableBody'); + logsTableBody.innerHTML = ''; + + data.events.forEach(event => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${new Date(event.timestamp).toLocaleString()} + ${event.event_type || '-'} + ${event.user_id ? userMap.get(event.user_id) || `User ${event.user_id}` : '-'} + ${event.ip_address || '-'} + `; + logsTableBody.appendChild(row); + }); + + // Update pagination + updateLogsPagination(data.current_page, data.pages); + } catch (error) { + console.error('Error changing logs page:', error); + showToast('Error loading logs', 'error'); + } +} + +// Function to change mails page +async function changeMailsPage(page) { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/mail-logs?page=${page}`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch mail logs'); + } + + const data = await response.json(); + const mailsTableBody = document.querySelector('#mails .table tbody'); + mailsTableBody.innerHTML = ''; + + data.mails.forEach(mail => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${new Date(mail.created_at).toLocaleString()} + ${mail.recipient} + ${mail.subject} + ${mail.sent_at ? new Date(mail.sent_at).toLocaleString() : '-'} + `; + mailsTableBody.appendChild(row); + }); + + // Update pagination + updateMailsPagination(data.current_page, data.pages); + } catch (error) { + console.error('Error changing mail logs page:', error); + showToast('Error loading mail logs', 'error'); + } +} + +// Function to view mail details +async function viewMailDetails(mailId) { + try { + // First get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + + if (!tokenResponse.ok) { + throw new Error('Failed to get management token'); + } + + const tokenData = await tokenResponse.json(); + if (!tokenData.token) { + throw new Error('No token received'); + } + + const response = await fetch(`{{ instance.main_url }}/api/admin/mail-logs/${mailId}`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch mail details'); + } + + const mail = await response.json(); + + // Update modal content + document.getElementById('modalRecipient').textContent = mail.recipient; + document.getElementById('modalStatus').innerHTML = ` + ${mail.status} + `; + document.getElementById('modalTemplate').textContent = mail.template_id ? mail.template_id : '-'; + document.getElementById('modalCreatedAt').textContent = new Date(mail.created_at).toLocaleString(); + document.getElementById('modalSentAt').textContent = mail.sent_at ? new Date(mail.sent_at).toLocaleString() : '-'; + document.getElementById('modalSubject').textContent = mail.subject; + document.getElementById('modalBody').innerHTML = mail.body; + + // Show modal + const modal = new bootstrap.Modal(document.getElementById('mailDetailsModal')); + modal.show(); + } catch (error) { + console.error('Error viewing mail details:', error); + showToast('Error loading mail details', 'error'); + } +} + +// Function to view log details +function viewLogDetails(event) { + const modal = new bootstrap.Modal(document.getElementById('logDetailsModal')); + const content = document.getElementById('logDetailsContent'); + + // Format the details in a readable way + let details = event.details; + let formattedDetails = 'No details available'; + + if (details) { + if (typeof details === 'string') { + formattedDetails = details.replace(/\n/g, '
'); + } else if (typeof details === 'object') { + formattedDetails = JSON.stringify(details, null, 2).replace(/\n/g, '
'); + } else { + formattedDetails = String(details); + } + } + + content.innerHTML = ` +
+ Timestamp: ${new Date(event.timestamp).toLocaleString()} +
+
+ Event Type: ${event.event_type || '-'} +
+
+ User: ${event.user_id ? userMap.get(event.user_id) || `User ${event.user_id}` : '-'} +
+
+ IP Address: ${event.ip_address || '-'} +
+
+ Details: +
+ ${formattedDetails} +
+
+ `; + + modal.show(); +} + +// Add event listener for clear filters button +document.addEventListener('DOMContentLoaded', function() { + // ... existing event listeners ... + + const clearLogFiltersBtn = document.getElementById('clearLogFiltersBtn'); + if (clearLogFiltersBtn) { + clearLogFiltersBtn.addEventListener('click', function() { + document.getElementById('logCategory').value = ''; + document.getElementById('dateRangeFilter').value = '7d'; + fetchLogs(); + }); + } +}); + +// Function to fetch version and payment plan info +async function fetchInstanceVersionAndPlan() { + const versionEl = document.getElementById('instance-version-value'); + const planEl = document.getElementById('instance-payment-plan-value'); + versionEl.textContent = 'Loading...'; + planEl.textContent = 'Loading...'; + try { + // Get JWT token + const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { + method: 'POST', + headers: { + 'X-API-Key': '{{ instance.connection_token }}', + 'Accept': 'application/json' + } + }); + if (!tokenResponse.ok) throw new Error('Failed to get management token'); + const tokenData = await tokenResponse.json(); + if (!tokenData.token) throw new Error('No token received'); + // Fetch version info + const response = await fetch(`{{ instance.main_url }}/api/admin/version-info`, { + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${tokenData.token}` + } + }); + if (!response.ok) throw new Error('Failed to fetch version info'); + const data = await response.json(); + versionEl.textContent = data.app_version || 'Unknown'; + planEl.textContent = data.pricing_tier_name || 'Unknown'; + } catch (error) { + versionEl.textContent = 'Error'; + planEl.textContent = 'Error'; + console.error('Error fetching version/plan info:', error); + } +} +document.addEventListener('DOMContentLoaded', function() { + // ... existing code ... + fetchInstanceVersionAndPlan(); +}); + diff --git a/templates/main/instance_detail.html b/templates/main/instance_detail.html index 2fe11d8..105897a 100644 --- a/templates/main/instance_detail.html +++ b/templates/main/instance_detail.html @@ -499,1143 +499,19 @@ {% block extra_js %} -{% endblock %} \ No newline at end of file + +{% endblock %} \ No newline at end of file diff --git a/templates/main/instances.html b/templates/main/instances.html index b458f8f..c14fdf1 100644 --- a/templates/main/instances.html +++ b/templates/main/instances.html @@ -649,5 +649,5 @@ {% endblock %} {% block extra_js %} - + {% endblock %} \ No newline at end of file