// DOM Elements const warrantyForm = document.getElementById('warrantyForm'); const settingsBtn = document.getElementById('settingsBtn'); const settingsMenu = document.getElementById('settingsMenu'); const darkModeToggle = document.getElementById('darkModeToggle'); const warrantiesList = document.getElementById('warrantiesList'); const refreshBtn = document.getElementById('refreshBtn'); const searchInput = document.getElementById('searchWarranties'); const statusFilter = document.getElementById('statusFilter'); const sortBySelect = document.getElementById('sortBy'); const exportBtn = document.getElementById('exportBtn'); const gridViewBtn = document.getElementById('gridViewBtn'); const listViewBtn = document.getElementById('listViewBtn'); const tableViewBtn = document.getElementById('tableViewBtn'); const tableViewHeader = document.querySelector('.table-view-header'); const fileInput = document.getElementById('invoice'); const fileName = document.getElementById('fileName'); const manualInput = document.getElementById('manual'); const manualFileName = document.getElementById('manualFileName'); const editModal = document.getElementById('editModal'); const deleteModal = document.getElementById('deleteModal'); const editWarrantyForm = document.getElementById('editWarrantyForm'); const saveWarrantyBtn = document.getElementById('saveWarrantyBtn'); const confirmDeleteBtn = document.getElementById('confirmDeleteBtn'); const loadingContainer = document.getElementById('loadingContainer'); const toastContainer = document.getElementById('toastContainer'); // Theme Management function setTheme(isDark) { document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light'); localStorage.setItem('darkMode', isDark); darkModeToggle.checked = isDark; } // Initialize theme based on user preference or system preference function initializeTheme() { const savedTheme = localStorage.getItem('darkMode'); if (savedTheme !== null) { // Use saved preference setTheme(savedTheme === 'true'); } else { // Check for system preference const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; setTheme(prefersDarkMode); } } // Initialize theme when page loads initializeTheme(); // Initialize page document.addEventListener('DOMContentLoaded', () => { // Ensure auth state is checked if (window.auth && window.auth.checkAuthState) { window.auth.checkAuthState(); } // Initialize settings button if (settingsBtn && settingsMenu) { settingsBtn.addEventListener('click', (e) => { e.stopPropagation(); settingsMenu.classList.toggle('active'); }); // Close settings menu when clicking outside document.addEventListener('click', (e) => { if (settingsMenu.classList.contains('active') && !settingsMenu.contains(e.target) && !settingsBtn.contains(e.target)) { settingsMenu.classList.remove('active'); } }); } // Initialize other event listeners and functionality // ... existing initialization code ... }); // Dark mode toggle darkModeToggle.addEventListener('change', (e) => { setTheme(e.target.checked); }); const serialNumbersContainer = document.getElementById('serialNumbersContainer'); // Add event listener for adding new serial number inputs serialNumbersContainer.addEventListener('click', (e) => { if (e.target.closest('.add-serial-number')) { addSerialNumberInput(); } }); function addSerialNumberInput(container = serialNumbersContainer) { // Check if this is the first input or an additional one const isFirstInput = container.querySelectorAll('.serial-number-input').length === 0; const newInput = document.createElement('div'); newInput.className = 'serial-number-input'; if (isFirstInput) { // First input should have both Add and Remove buttons newInput.innerHTML = ` `; // Add event listener for the Add button newInput.querySelector('.add-serial-number').addEventListener('click', function(e) { e.stopPropagation(); // Stop event from bubbling up to the container addSerialNumberInput(container); }); } else { // Additional inputs should have only Remove button newInput.innerHTML = ` `; // Add remove button functionality newInput.querySelector('.remove-serial-number').addEventListener('click', function() { this.parentElement.remove(); }); } container.appendChild(newInput); } // Variables let warranties = []; let currentWarrantyId = null; let currentFilters = { search: '', status: 'all', sort: 'expiration' }; let currentView = 'grid'; // Default view // API URL const API_URL = '/api/warranties'; // Event Listeners document.addEventListener('DOMContentLoaded', () => { console.log('DOM fully loaded, initializing app...'); // Initialize the app initializeTheme(); // Load warranties loadWarranties(); // Initialize form tabs initFormTabs(); // Initialize the form resetForm(); // Close modals when clicking outside or on close button document.querySelectorAll('.modal-backdrop, [data-dismiss="modal"]').forEach(element => { element.addEventListener('click', (e) => { if (e.target === element) { closeModals(); } }); }); // Prevent modal content clicks from closing the modal document.querySelectorAll('.modal').forEach(modal => { modal.addEventListener('click', (e) => { e.stopPropagation(); }); }); // Filter event listeners if (searchInput) { searchInput.addEventListener('input', () => { currentFilters.search = searchInput.value.toLowerCase(); applyFilters(); }); } if (statusFilter) { statusFilter.addEventListener('change', () => { currentFilters.status = statusFilter.value; applyFilters(); }); } if (sortBySelect) { sortBySelect.addEventListener('change', () => { currentFilters.sort = sortBySelect.value; applyFilters(); }); } // View switcher event listeners if (gridViewBtn) gridViewBtn.addEventListener('click', () => switchView('grid')); if (listViewBtn) listViewBtn.addEventListener('click', () => switchView('list')); if (tableViewBtn) tableViewBtn.addEventListener('click', () => switchView('table')); // Export button event listener if (exportBtn) exportBtn.addEventListener('click', exportWarranties); // File input change event if (fileInput) fileInput.addEventListener('change', (e) => updateFileName(e, 'invoice', 'fileName')); if (manualInput) manualInput.addEventListener('change', (e) => updateFileName(e, 'manual', 'manualFileName')); const editInvoice = document.getElementById('editInvoice'); if (editInvoice) { editInvoice.addEventListener('change', () => { updateFileName(null, 'editInvoice', 'editFileName'); }); } const editManual = document.getElementById('editManual'); if (editManual) { editManual.addEventListener('change', () => { updateFileName(null, 'editManual', 'editManualFileName'); }); } // Form submission if (warrantyForm) warrantyForm.addEventListener('submit', addWarranty); // Refresh button if (refreshBtn) refreshBtn.addEventListener('click', loadWarranties); // Save warranty changes if (saveWarrantyBtn) saveWarrantyBtn.addEventListener('click', updateWarranty); // Confirm delete button if (confirmDeleteBtn) confirmDeleteBtn.addEventListener('click', deleteWarranty); // Load saved view preference loadViewPreference(); console.log('App initialization complete'); }); // Functions function showLoading() { loadingContainer.classList.add('active'); } function hideLoading() { loadingContainer.classList.remove('active'); } function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.innerHTML = ` ${message} `; // Add close event toast.querySelector('.toast-close').addEventListener('click', () => { toast.remove(); }); toastContainer.appendChild(toast); // Auto remove after 3 seconds setTimeout(() => { if (toast.parentElement) { toast.remove(); } }, 3000); } function updateFileName(event, inputId = 'invoice', outputId = 'fileName') { const input = document.getElementById(inputId); const output = document.getElementById(outputId); if (input.files.length > 0) { output.textContent = input.files[0].name; } else { output.textContent = ''; } } // Helper function to process warranty data function processWarrantyData(warranty) { console.log('Processing warranty data:', warranty); // Create a copy of the warranty object to avoid modifying the original const processedWarranty = { ...warranty }; // Ensure product_name exists if (!processedWarranty.product_name) { processedWarranty.product_name = 'Unnamed Product'; } const today = new Date(); // Handle purchase date let purchaseDate = null; if (processedWarranty.purchase_date) { purchaseDate = new Date(processedWarranty.purchase_date); // Check if date is valid if (isNaN(purchaseDate.getTime())) { purchaseDate = null; } } processedWarranty.purchaseDate = purchaseDate; // Handle expiration date let expirationDate = null; if (processedWarranty.expiration_date) { expirationDate = new Date(processedWarranty.expiration_date); // Check if date is valid if (isNaN(expirationDate.getTime())) { expirationDate = null; } } processedWarranty.expirationDate = expirationDate; // Calculate days remaining only if expiration date is valid let daysRemaining = null; if (expirationDate && !isNaN(expirationDate.getTime())) { daysRemaining = Math.floor((expirationDate - today) / (1000 * 60 * 60 * 24)); } let statusClass = 'active'; let statusText = 'Active'; if (daysRemaining === null) { statusClass = 'unknown'; statusText = 'Unknown status'; } else if (daysRemaining < 0) { statusClass = 'expired'; statusText = 'Expired'; } else if (daysRemaining < 30) { statusClass = 'expiring'; statusText = `Expiring Soon (${daysRemaining} days)`; } else { statusText = `${daysRemaining} days remaining`; } // Add status to warranty object processedWarranty.status = statusClass; processedWarranty.daysRemaining = daysRemaining; processedWarranty.statusText = statusText; console.log('Processed warranty data result:', processedWarranty); return processedWarranty; } // Function to process all warranties in the array function processAllWarranties() { console.log('Processing all warranties in array...'); if (warranties && warranties.length > 0) { warranties = warranties.map(warranty => processWarrantyData(warranty)); } console.log('Processed warranties:', warranties); } async function loadWarranties() { try { console.log('Loading warranties...'); showLoading(); // Use the full URL to avoid path issues const apiUrl = window.location.origin + '/api/warranties'; // Check if auth is available and user is authenticated if (!window.auth || !window.auth.isAuthenticated()) { console.log('User not authenticated, showing empty state'); renderEmptyState('Please log in to view your warranties.'); hideLoading(); return; } // Get the auth token const token = window.auth.getToken(); if (!token) { console.log('No auth token available'); renderEmptyState('Authentication error. Please log in again.'); hideLoading(); return; } // Create request with auth header const options = { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }; console.log('Fetching warranties with auth token'); const response = await fetch(apiUrl, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: `HTTP error ${response.status}` })); console.error('Error loading warranties:', response.status, errorData); throw new Error(`Error loading warranties: ${errorData.message || response.status}`); } const data = await response.json(); console.log('Received warranties from server:', data); // Process each warranty to calculate status and days remaining warranties = data.map(warranty => { return processWarrantyData(warranty); }); console.log('Processed warranties:', warranties); if (warranties.length === 0) { console.log('No warranties found, showing empty state'); renderEmptyState('No warranties found. Add your first warranty using the form.'); } else { console.log('Applying filters to display warranties'); applyFilters(); } } catch (error) { console.error('Error loading warranties:', error); renderEmptyState('Error loading warranties. Please try again later.'); } finally { hideLoading(); } } function renderEmptyState(message = 'No warranties yet. Add your first warranty to get started.') { warrantiesList.innerHTML = `

No warranties found

${message}

`; } function formatDate(date) { if (!date) return 'N/A'; // If date is already a Date object, use it directly const dateObj = date instanceof Date ? date : new Date(date); // Check if date is valid if (isNaN(dateObj.getTime())) { return 'N/A'; } return dateObj.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } async function renderWarranties(warrantiesToRender) { console.log('renderWarranties called with:', warrantiesToRender); if (!warrantiesToRender || warrantiesToRender.length === 0) { renderEmptyState(); return; } const today = new Date(); warrantiesList.innerHTML = ''; // Apply sorting based on current sort selection const sortedWarranties = [...warrantiesToRender].sort((a, b) => { switch (currentFilters.sort) { case 'name': return a.product_name.localeCompare(b.product_name); case 'purchase': return new Date(b.purchase_date || 0) - new Date(a.purchase_date || 0); case 'expiration': default: const dateA = new Date(a.expiration_date || 0); const dateB = new Date(b.expiration_date || 0); const isExpiredA = dateA < today; const isExpiredB = dateB < today; if (isExpiredA && !isExpiredB) return 1; if (!isExpiredA && isExpiredB) return -1; // Both active or both expired, sort by date return dateA - dateB; } }); console.log('Sorted warranties:', sortedWarranties); // Update the container class based on current view warrantiesList.className = `warranties-list ${currentView}-view`; // Show/hide table header for table view if (tableViewHeader) { tableViewHeader.classList.toggle('visible', currentView === 'table'); } // Update view buttons to reflect current view if (gridViewBtn && listViewBtn && tableViewBtn) { gridViewBtn.classList.toggle('active', currentView === 'grid'); listViewBtn.classList.toggle('active', currentView === 'list'); tableViewBtn.classList.toggle('active', currentView === 'table'); } sortedWarranties.forEach(warranty => { // Use the pre-processed dates from the warranty object const purchaseDate = warranty.purchaseDate; const expirationDate = warranty.expirationDate; // Use the pre-calculated status and days remaining from the warranty object const statusClass = warranty.status || 'unknown'; const statusText = warranty.statusText || 'Unknown status'; // Make sure serial numbers array exists and is valid const validSerialNumbers = Array.isArray(warranty.serial_numbers) ? warranty.serial_numbers.filter(sn => sn && typeof sn === 'string' && sn.trim() !== '') : []; const cardElement = document.createElement('div'); cardElement.className = `warranty-card ${statusClass === 'expired' ? 'expired' : statusClass === 'expiring' ? 'expiring-soon' : 'active'}`; // Create different HTML structure based on the current view if (currentView === 'grid') { // Grid view HTML structure cardElement.innerHTML = `

${warranty.product_name || 'Unnamed Product'}

Purchased: ${formatDate(purchaseDate)}
Warranty: ${warranty.warranty_years !== undefined ? `${warranty.warranty_years} ${warranty.warranty_years === 1 ? 'year' : 'years'}` : 'N/A'}
Expires: ${formatDate(expirationDate)}
${warranty.purchase_price ? `
Price: $${parseFloat(warranty.purchase_price).toFixed(2)}
` : ''} ${validSerialNumbers.length > 0 ? `
Serial Numbers:
    ${validSerialNumbers.map(sn => `
  • ${sn}
  • `).join('')}
` : ''}
${statusText}
`; } else if (currentView === 'list') { // List view HTML structure cardElement.innerHTML = `

${warranty.product_name || 'Unnamed Product'}

Purchased: ${formatDate(purchaseDate)}
Warranty: ${warranty.warranty_years !== undefined ? `${warranty.warranty_years} ${warranty.warranty_years === 1 ? 'year' : 'years'}` : 'N/A'}
Expires: ${formatDate(expirationDate)}
${warranty.purchase_price ? `
Price: $${parseFloat(warranty.purchase_price).toFixed(2)}
` : ''} ${validSerialNumbers.length > 0 ? `
Serial Numbers:
    ${validSerialNumbers.map(sn => `
  • ${sn}
  • `).join('')}
` : ''}
${statusText}
`; } else if (currentView === 'table') { // Table view HTML structure cardElement.innerHTML = `

${warranty.product_name || 'Unnamed Product'}

Purchased: ${formatDate(purchaseDate)}
Expires: ${formatDate(expirationDate)}
${statusText}
`; } // Add event listeners warrantiesList.appendChild(cardElement); // Edit button event listener cardElement.querySelector('.edit-btn').addEventListener('click', () => { openEditModal(warranty); }); // Delete button event listener cardElement.querySelector('.delete-btn').addEventListener('click', () => { openDeleteModal(warranty.id); }); }); } function filterWarranties() { const searchTerm = searchInput.value.toLowerCase(); if (!searchTerm) { renderWarranties(); return; } const filtered = warranties.filter(warranty => warranty.product_name.toLowerCase().includes(searchTerm) ); renderWarranties(filtered); } function openEditModal(warranty) { currentWarrantyId = warranty.id; // Populate form fields document.getElementById('editProductName').value = warranty.product_name; document.getElementById('editProductUrl').value = warranty.product_url || ''; document.getElementById('editPurchaseDate').value = warranty.purchase_date.split('T')[0]; document.getElementById('editWarrantyYears').value = warranty.warranty_years; document.getElementById('editPurchasePrice').value = warranty.purchase_price || ''; // Clear existing serial number inputs const editSerialNumbersContainer = document.getElementById('editSerialNumbersContainer'); editSerialNumbersContainer.innerHTML = ''; // Add event listener for adding new serial number inputs in edit modal editSerialNumbersContainer.addEventListener('click', (e) => { if (e.target.closest('.add-serial-number')) { addSerialNumberInput(editSerialNumbersContainer); } }); const validSerialNumbers = Array.isArray(warranty.serial_numbers) ? warranty.serial_numbers.filter(sn => sn && typeof sn === 'string' && sn.trim() !== '') : []; if (validSerialNumbers.length === 0) { // Add a single empty input if there are no serial numbers addSerialNumberInput(editSerialNumbersContainer); } else { // Add the first serial number with an "Add Another" button const firstInput = document.createElement('div'); firstInput.className = 'serial-number-input'; firstInput.innerHTML = ` `; // Add event listener for the Add button firstInput.querySelector('.add-serial-number').addEventListener('click', function(e) { e.stopPropagation(); // Stop event from bubbling up addSerialNumberInput(editSerialNumbersContainer); }); editSerialNumbersContainer.appendChild(firstInput); // Add the rest of the serial numbers with "Remove" buttons for (let i = 1; i < validSerialNumbers.length; i++) { const newInput = document.createElement('div'); newInput.className = 'serial-number-input'; newInput.innerHTML = ` `; // Add remove button functionality newInput.querySelector('.remove-serial-number').addEventListener('click', function() { this.parentElement.remove(); }); editSerialNumbersContainer.appendChild(newInput); } } // Show current invoice if exists const currentInvoiceElement = document.getElementById('currentInvoice'); if (currentInvoiceElement) { if (warranty.invoice_path) { currentInvoiceElement.innerHTML = ` Current invoice: View (Upload a new file to replace) `; } else { currentInvoiceElement.innerHTML = 'No invoice uploaded'; } } // Show current manual if exists const currentManualElement = document.getElementById('currentManual'); if (currentManualElement) { if (warranty.manual_path) { currentManualElement.innerHTML = ` Current manual: View (Upload a new file to replace) `; } else { currentManualElement.innerHTML = 'No manual uploaded'; } } // Reset file input display document.getElementById('editFileName').textContent = ''; // Show modal editModal.classList.add('active'); } function openDeleteModal(warrantyId) { currentWarrantyId = warrantyId; deleteModal.classList.add('active'); } function closeModals() { editModal.classList.remove('active'); deleteModal.classList.remove('active'); currentWarrantyId = null; } async function updateWarranty() { // Check if user is authenticated if (window.auth && !window.auth.isAuthenticated()) { showToast('Please login to update warranties', 'warning'); closeModals(); setTimeout(() => { window.location.href = 'login.html'; }, 1500); return; } if (!currentWarrantyId) { showToast('No warranty selected for update', 'error'); return; } // Create FormData object const formData = new FormData(editWarrantyForm); // Get all serial numbers const serialNumbers = []; document.querySelectorAll('#editSerialNumbersContainer input[name="serial_numbers[]"]').forEach(input => { if (input.value.trim()) { serialNumbers.push(input.value.trim()); } }); // Remove default serial numbers entry and add the collected ones formData.delete('serial_numbers[]'); serialNumbers.forEach(sn => { formData.append('serial_numbers', sn); }); try { showLoading(); // Get the auth token const token = window.auth.getToken(); if (!token) { showToast('Authentication error. Please log in again.', 'error'); hideLoading(); closeModals(); return; } // Create request with auth header const options = { method: 'PUT', body: formData, headers: { 'Authorization': `Bearer ${token}` } }; // Use the full URL to avoid path issues const apiUrl = `${window.location.origin}/api/warranties/${currentWarrantyId}`; console.log('Updating warranty with ID:', currentWarrantyId); const response = await fetch(apiUrl, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: `HTTP error ${response.status}` })); throw new Error(errorData.message || `Error updating warranty: ${response.status}`); } const result = await response.json(); console.log('Received update warranty result:', result); // Reload all warranties to get the complete data await loadWarranties(); // Close modal closeModals(); // Show success message showToast('Warranty updated successfully!', 'success'); } catch (error) { console.error('Error updating warranty:', error); showToast(error.message || 'Error updating warranty. Please try again.', 'error'); } finally { hideLoading(); } } async function deleteWarranty() { // Check if user is authenticated if (window.auth && !window.auth.isAuthenticated()) { showToast('Please login to delete warranties', 'warning'); closeModals(); setTimeout(() => { window.location.href = 'login.html'; }, 1500); return; } if (!currentWarrantyId) { showToast('No warranty selected for deletion', 'error'); return; } try { showLoading(); // Get the auth token const token = window.auth.getToken(); if (!token) { showToast('Authentication error. Please log in again.', 'error'); hideLoading(); closeModals(); return; } // Create request with auth header const options = { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }; // Use the full URL to avoid path issues const apiUrl = `${window.location.origin}/api/warranties/${currentWarrantyId}`; console.log('Deleting warranty with ID:', currentWarrantyId); const response = await fetch(apiUrl, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: `HTTP error ${response.status}` })); throw new Error(errorData.message || `Error deleting warranty: ${response.status}`); } // Reload all warranties to get the updated list await loadWarranties(); // Close the modal closeModals(); // Show success message showToast('Warranty deleted successfully!', 'success'); } catch (error) { console.error('Error deleting warranty:', error); showToast(error.message || 'Error deleting warranty. Please try again.', 'error'); } finally { hideLoading(); } } // Form tab navigation variables const formTabs = Array.from(document.querySelectorAll('.form-tab')); const tabContents = Array.from(document.querySelectorAll('.tab-content')); let currentTabIndex = 0; // Initialize form tabs function initFormTabs() { // Hide the submit button initially const submitButton = document.querySelector('button[type="submit"]'); if (submitButton) { submitButton.style.display = 'none'; } // Show/hide navigation buttons based on current tab updateNavigationButtons(); // Add event listeners for tab navigation document.querySelector('.next-tab').addEventListener('click', () => { if (validateTab(currentTabIndex)) { switchToTab(currentTabIndex + 1); } else { showValidationErrors(currentTabIndex); } }); document.querySelector('.prev-tab').addEventListener('click', () => { switchToTab(currentTabIndex - 1); }); // Add click event for tab headers formTabs.forEach((tab, index) => { tab.addEventListener('click', () => { // Only allow clicking on previous tabs or current tab if (index <= currentTabIndex) { switchToTab(index); } }); }); } // Switch to a specific tab function switchToTab(index) { // Ensure index is within bounds if (index < 0 || index >= formTabs.length) return; // Update active tab formTabs.forEach(tab => tab.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); formTabs[index].classList.add('active'); tabContents[index].classList.add('active'); // Update current tab index currentTabIndex = index; // Update progress indicator document.querySelector('.form-tabs').setAttribute('data-step', currentTabIndex); // Update completed tabs updateCompletedTabs(); // Update navigation buttons updateNavigationButtons(); // Update summary if on summary tab if (index === formTabs.length - 1) { updateSummary(); } } // Update navigation buttons based on current tab function updateNavigationButtons() { const prevButton = document.querySelector('.prev-tab'); const nextButton = document.querySelector('.next-tab'); const submitButton = document.querySelector('button[type="submit"]'); // Hide/show previous button prevButton.style.display = currentTabIndex === 0 ? 'none' : 'block'; // Hide/show next button and submit button if (currentTabIndex === formTabs.length - 1) { nextButton.style.display = 'none'; submitButton.style.display = 'block'; } else { nextButton.style.display = 'block'; submitButton.style.display = 'none'; } } // Update completed tabs function updateCompletedTabs() { formTabs.forEach((tab, index) => { if (index < currentTabIndex) { tab.classList.add('completed'); } else { tab.classList.remove('completed'); } }); } // Validate a specific tab function validateTab(tabIndex) { const tabContent = tabContents[tabIndex]; const requiredInputs = tabContent.querySelectorAll('input[required]'); let isValid = true; requiredInputs.forEach(input => { if (!input.value.trim()) { isValid = false; input.classList.add('invalid'); } else { input.classList.remove('invalid'); } }); return isValid; } // Show validation errors for a specific tab function showValidationErrors(tabIndex) { const tabContent = tabContents[tabIndex]; const requiredInputs = tabContent.querySelectorAll('input[required]'); requiredInputs.forEach(input => { if (!input.value.trim()) { input.classList.add('invalid'); // Add validation message if not already present let validationMessage = input.nextElementSibling; if (!validationMessage || !validationMessage.classList.contains('validation-message')) { validationMessage = document.createElement('div'); validationMessage.className = 'validation-message'; validationMessage.textContent = 'This field is required'; input.parentNode.insertBefore(validationMessage, input.nextSibling); } } }); // Show toast message showToast('Please fill in all required fields', 'error'); } // Update summary tab with current form values function updateSummary() { // Product information document.getElementById('summary-product-name').textContent = document.getElementById('productName').value || '-'; document.getElementById('summary-product-url').textContent = document.getElementById('productUrl').value || '-'; // Serial numbers const serialNumbers = []; document.querySelectorAll('input[name="serial_numbers[]"]').forEach(input => { if (input.value.trim()) { serialNumbers.push(input.value.trim()); } }); const serialNumbersContainer = document.getElementById('summary-serial-numbers'); if (serialNumbers.length > 0) { serialNumbersContainer.innerHTML = ''; } else { serialNumbersContainer.textContent = 'None'; } // Warranty details const purchaseDate = document.getElementById('purchaseDate').value; document.getElementById('summary-purchase-date').textContent = purchaseDate ? new Date(purchaseDate).toLocaleDateString() : '-'; const warrantyYears = document.getElementById('warrantyYears').value; document.getElementById('summary-warranty-years').textContent = warrantyYears ? `${warrantyYears} ${warrantyYears > 1 ? 'years' : 'year'}` : '-'; // Calculate and display expiration date if (purchaseDate && warrantyYears) { const expirationDate = new Date(purchaseDate); expirationDate.setFullYear(expirationDate.getFullYear() + parseInt(warrantyYears)); document.getElementById('summary-expiration-date').textContent = expirationDate.toLocaleDateString(); } else { document.getElementById('summary-expiration-date').textContent = '-'; } // Purchase price const purchasePrice = document.getElementById('purchasePrice').value; document.getElementById('summary-purchase-price').textContent = purchasePrice ? `$${parseFloat(purchasePrice).toFixed(2)}` : 'Not specified'; // Documents const invoiceFile = document.getElementById('invoice').files[0]; document.getElementById('summary-invoice').textContent = invoiceFile ? invoiceFile.name : 'No file selected'; const manualFile = document.getElementById('manual').files[0]; document.getElementById('summary-manual').textContent = manualFile ? manualFile.name : 'No file selected'; } // Add input event listeners to remove validation errors when user types document.addEventListener('input', (e) => { if (e.target.hasAttribute('required') && e.target.classList.contains('invalid')) { if (e.target.value.trim()) { e.target.classList.remove('invalid'); // Remove validation message if exists const validationMessage = e.target.nextElementSibling; if (validationMessage && validationMessage.classList.contains('validation-message')) { validationMessage.remove(); } } } }); // Function to reset the form and initialize serial number inputs function resetForm() { // Reset the form warrantyForm.reset(); // Reset serial numbers container serialNumbersContainer.innerHTML = ''; // Add the first serial number input addSerialNumberInput(); // Reset form tabs currentTabIndex = 0; switchToTab(0); // Clear any file input displays fileName.textContent = ''; manualFileName.textContent = ''; } async function addWarranty(event) { event.preventDefault(); // Check authentication first if (!window.auth || !window.auth.isAuthenticated()) { showToast('Please log in to add warranties', 'error'); return; } // Validate the current tab if (!validateTab(currentTabIndex)) { return; } try { showLoading(); // Create FormData object const formData = new FormData(warrantyForm); // Add serial numbers const serialNumberInputs = document.querySelectorAll('input[name="serial_numbers[]"]'); formData.delete('serial_numbers[]'); // Remove the default entries serialNumberInputs.forEach(input => { if (input.value.trim()) { formData.append('serial_numbers', input.value.trim()); } }); // Get the auth token const token = window.auth.getToken(); if (!token) { showToast('Authentication error. Please log in again.', 'error'); hideLoading(); return; } // Create request with auth header const options = { method: 'POST', body: formData, headers: { 'Authorization': `Bearer ${token}` } }; // Use the full URL to avoid path issues const apiUrl = window.location.origin + '/api/warranties'; console.log('Sending warranty data to server...'); const response = await fetch(apiUrl, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: `HTTP error ${response.status}` })); throw new Error(errorData.message || `Error adding warranty: ${response.status}`); } const result = await response.json(); console.log('Received add warranty result:', result); // The server only returns the ID, so we need to fetch the complete warranty data if (result.id) { console.log('Fetching complete warranty data for ID:', result.id); // Reload all warranties to get the complete data await loadWarranties(); // Reset the form and initialize serial number inputs resetForm(); // Show success message showToast('Warranty added successfully!', 'success'); } else { throw new Error('Failed to get warranty ID from server response'); } } catch (error) { console.error('Error adding warranty:', error); showToast(error.message || 'Error adding warranty. Please try again.', 'error'); } finally { hideLoading(); } } function applyFilters() { console.log('applyFilters called with warranties:', warranties); const filtered = warranties.filter(warranty => { // Status filter if (currentFilters.status !== 'all' && warranty.status !== currentFilters.status) { return false; } // Search filter if (currentFilters.search && !warranty.product_name.toLowerCase().includes(currentFilters.search.toLowerCase())) { return false; } return true; }); console.log('Filtered warranties:', filtered); renderWarranties(filtered); } async function exportWarranties() { // Get filtered warranties let warrantiesToExport = [...warranties]; // Apply current filters if (currentFilters.search) { warrantiesToExport = warrantiesToExport.filter(warranty => warranty.product_name.toLowerCase().includes(currentFilters.search) ); } if (currentFilters.status !== 'all') { warrantiesToExport = warrantiesToExport.filter(warranty => warranty.status === currentFilters.status ); } // Create CSV content let csvContent = "data:text/csv;charset=utf-8,"; // Add headers csvContent += "Product Name,Purchase Date,Warranty Period,Expiration Date,Status,Serial Numbers\n"; // Add data rows warrantiesToExport.forEach(warranty => { const purchaseDate = new Date(warranty.purchase_date).toLocaleDateString(); const expirationDate = new Date(warranty.expiration_date).toLocaleDateString(); const serialNumbers = Array.isArray(warranty.serial_numbers) ? warranty.serial_numbers.join('; ') : ''; // Calculate status if not already set if (!warranty.status) { const today = new Date(); const expDate = new Date(warranty.expiration_date); const daysRemaining = Math.floor((expDate - today) / (1000 * 60 * 60 * 24)); if (daysRemaining < 0) { warranty.status = 'Expired'; } else if (daysRemaining < 30) { warranty.status = 'Expiring Soon'; } else { warranty.status = 'Active'; } } // Format status for CSV let statusText = warranty.status.charAt(0).toUpperCase() + warranty.status.slice(1); if (statusText === 'Expiring') statusText = 'Expiring Soon'; // Create CSV row csvContent += `"${warranty.product_name}",${purchaseDate},${warranty.warranty_years} years,${expirationDate},${statusText},"${serialNumbers}"\n`; }); // Create download link const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", "warranties.csv"); document.body.appendChild(link); // Trigger download link.click(); // Clean up document.body.removeChild(link); showToast('Warranties exported successfully', 'success'); } // Function to switch between views function switchView(viewType) { console.log('Switching view to:', viewType); // Process all warranties to ensure consistency processAllWarranties(); // Update current view currentView = viewType; // Update view buttons gridViewBtn.classList.toggle('active', viewType === 'grid'); listViewBtn.classList.toggle('active', viewType === 'list'); tableViewBtn.classList.toggle('active', viewType === 'table'); // Update warranties list class warrantiesList.className = `warranties-list ${viewType}-view`; // Show/hide table header for table view if (tableViewHeader) { tableViewHeader.classList.toggle('visible', viewType === 'table'); } // Re-render warranties with the new view console.log('Applying filters after switching view...'); applyFilters(); // Save view preference to localStorage localStorage.setItem('warrantyView', viewType); } // Load saved view preference function loadViewPreference() { // First check for warrantyView (direct view selection) const savedView = localStorage.getItem('warrantyView'); // If not found, check for defaultView (from settings) const defaultView = localStorage.getItem('defaultView'); // Use warrantyView if available, otherwise use defaultView, or fall back to grid const viewToUse = savedView || defaultView || 'grid'; if (viewToUse) { switchView(viewToUse); } }