diff --git a/templates/main/instance_detail.html b/templates/main/instance_detail.html index 240ed62..ab733e6 100644 --- a/templates/main/instance_detail.html +++ b/templates/main/instance_detail.html @@ -93,6 +93,16 @@ Contacts + +
@@ -170,6 +180,7 @@ Phone Company Position + Role Status Actions @@ -182,6 +193,115 @@
+ + +
+
+
+
+
+
+
Activity Log
+
+ + + +
+
+ + +
+ + + + + + + + + + + + +
TimestampEvent TypeUserIP Address
+
+ + + +
+
+
+
+
+ + +
+
+
+
+
Mail Log
+
+ + + +
+
+ + +
+ + + + + + + + + + + + +
Created AtRecipientSubjectSent At
+
+ + + +
+
+
@@ -318,6 +438,36 @@ + + + + + + {% endblock %} {% block extra_js %} @@ -461,22 +611,17 @@ async function fetchCompanyInfo() { // Update company name separately with debugging const companyNameElement = document.getElementById('company-name-value'); const companyNameLabel = document.getElementById('company-name-label'); - console.log('Before update - Label:', companyNameLabel?.textContent); - console.log('Before update - Value:', companyNameElement?.textContent); + + // Ensure label text is set and maintained + if (companyNameLabel) { + companyNameLabel.textContent = 'Company Name:'; + } if (companyNameElement) { companyNameElement.textContent = data.company_name || 'Not set'; } - // Ensure label text is set - if (companyNameLabel && !companyNameLabel.textContent) { - companyNameLabel.textContent = 'Company Name:'; - } - - console.log('After update - Label:', companyNameLabel?.textContent); - console.log('After update - Value:', companyNameElement?.textContent); - - // Update company details + // Update other company details const updateElement = (id, value) => { const element = document.getElementById(id); if (element && element.classList.contains('company-value')) { @@ -594,7 +739,6 @@ window.addEventListener('beforeunload', function() { async function fetchContacts() { try { // First get JWT token - console.log('Getting management token for contacts...'); const tokenResponse = await fetch(`{{ instance.main_url }}/api/admin/management-token`, { method: 'POST', headers: { @@ -612,7 +756,6 @@ async function fetchContacts() { throw new Error('No token received'); } - // Then fetch contacts using the JWT token const response = await fetch(`{{ instance.main_url }}/api/admin/contacts`, { headers: { 'Accept': 'application/json', @@ -628,29 +771,44 @@ async function fetchContacts() { const contactsList = document.getElementById('contactsTableBody'); contactsList.innerHTML = ''; - if (contacts.length === 0) { - contactsList.innerHTML = ` -
- -

No contacts found

-
- `; - return; - } - 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.email || 'Not set'} - ${contact.phone || 'Not set'} - ${contact.company || 'Not set'} - ${contact.position || 'Not set'} + ${contact.username} ${contact.last_name} - - ${contact.is_active ? 'Active' : 'Inactive'} - + + + ${contact.email} + + + + ${contact.phone ? ` + + + ${contact.phone} + + + ` : '-'} + + ${contact.company || '-'} + ${contact.position || '-'} + ${contact.role.charAt(0).toUpperCase() + contact.role.slice(1)} + ${contact.is_active ? 'Active' : 'Inactive'}
+ `; + 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(); + }); + } +}); {% endblock %} \ No newline at end of file