// 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 = `