// Global variables
let warranties = [];
let currentTabIndex = 0;
let tabContents;
let editMode = false;
let currentWarrantyId = null;
let currentFilters = {
status: 'all',
tag: 'all',
search: '',
sortBy: 'expiration'
};
// Tag related variables
let allTags = [];
let selectedTags = []; // Will hold objects with id, name, color
// Global variable for edit mode tags
let editSelectedTags = [];
// 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 clearSearchBtn = document.getElementById('clearSearch');
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');
// Tag DOM Elements
const selectedTagsContainer = document.getElementById('selectedTags');
const tagSearch = document.getElementById('tagSearch');
const tagsList = document.getElementById('tagsList');
const manageTagsBtn = document.getElementById('manageTagsBtn');
const tagManagementModal = document.getElementById('tagManagementModal');
const newTagForm = document.getElementById('newTagForm');
const existingTagsContainer = document.getElementById('existingTags');
/**
* Get current user type (admin or user)
* @returns {string} 'admin' or 'user'
*/
function getUserType() {
try {
const userInfo = JSON.parse(localStorage.getItem('user_info') || '{}');
return userInfo.is_admin === true ? 'admin' : 'user';
} catch (e) {
console.error('Error determining user type:', e);
return 'user'; // Default to user if we can't determine
}
}
/**
* Get the appropriate localStorage key prefix based on user type
* @returns {string} The prefix to use for localStorage keys
*/
function getPreferenceKeyPrefix() {
return getUserType() === 'admin' ? 'admin_' : 'user_';
}
// Theme Management
function setTheme(isDark) {
// Get the appropriate key prefix based on user type
const prefix = getPreferenceKeyPrefix();
console.log(`Setting theme with prefix: ${prefix}`);
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
// Update darkMode settings
localStorage.setItem(`${prefix}darkMode`, isDark);
localStorage.setItem('darkMode', isDark); // Keep for backward compatibility
// Update DOM
if (isDark) {
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
// Set toggle state
if (darkModeToggle) {
darkModeToggle.checked = isDark;
}
// Also update preferences in localStorage for consistency
try {
let userPrefs = {};
const storedPrefs = localStorage.getItem(`${prefix}preferences`);
if (storedPrefs) {
userPrefs = JSON.parse(storedPrefs);
}
userPrefs.theme = isDark ? 'dark' : 'light';
localStorage.setItem(`${prefix}preferences`, JSON.stringify(userPrefs));
} catch (e) {
console.error(`Error updating theme in ${prefix}preferences:`, e);
}
}
// Initialize theme based on user preference or system preference
function initializeTheme() {
// Get the appropriate key prefix based on user type
const prefix = getPreferenceKeyPrefix();
console.log(`Initializing theme with prefix: ${prefix}`);
// First check user-specific setting
const userDarkMode = localStorage.getItem(`${prefix}darkMode`);
if (userDarkMode !== null) {
console.log(`Found user-specific dark mode setting: ${userDarkMode}`);
setTheme(userDarkMode === 'true');
return;
}
// Then check global setting for backward compatibility
const globalDarkMode = localStorage.getItem('darkMode');
if (globalDarkMode !== null) {
console.log(`Found global dark mode setting: ${globalDarkMode}`);
setTheme(globalDarkMode === 'true');
return;
}
// Check for system preference if no stored preference
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
console.log(`No saved preference, using system preference: ${prefersDarkMode}`);
setTheme(prefersDarkMode);
}
// Initialize theme when page loads
initializeTheme();
// Variables
let currentView = 'grid'; // Default view
let expiringSoonDays = 30; // Default value, will be updated from user preferences
// API URL
const API_URL = '/api/warranties';
// Form tab navigation variables
const formTabs = Array.from(document.querySelectorAll('.form-tab'));
const tabContentsArray = document.querySelectorAll('.tab-content');
tabContents = Array.from(tabContentsArray); // Convert NodeList to Array
const nextButton = document.querySelector('.next-tab');
const prevButton = document.querySelector('.prev-tab');
// Initialize form tabs
function initFormTabs() {
console.log('Initializing form tabs...');
// 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();
// Remove any existing event listeners before adding new ones
if (nextButton && prevButton) {
const nextTabClone = nextButton.cloneNode(true);
const prevTabClone = prevButton.cloneNode(true);
nextButton.parentNode.replaceChild(nextTabClone, nextButton);
prevButton.parentNode.replaceChild(prevTabClone, prevButton);
// Add event listeners for tab navigation
document.querySelector('.next-tab').addEventListener('click', () => {
console.log('Next button clicked, current tab:', currentTabIndex);
if (validateTab(currentTabIndex)) {
switchToTab(currentTabIndex + 1);
} else {
showValidationErrors(currentTabIndex);
}
});
document.querySelector('.prev-tab').addEventListener('click', () => {
console.log('Previous button clicked, current tab:', currentTabIndex);
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) {
console.log(`Switching to tab ${index} from tab ${currentTabIndex}`);
// Ensure index is within bounds
if (index < 0 || index >= formTabs.length) {
console.log(`Invalid tab index: ${index}, not switching`);
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]');
// If there are no required inputs in this tab, it's automatically valid
if (requiredInputs.length === 0) {
return true;
}
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
const summaryProductName = document.getElementById('summary-product-name');
if (summaryProductName) {
summaryProductName.textContent =
document.getElementById('productName')?.value || '-';
}
const summaryProductUrl = document.getElementById('summary-product-url');
if (summaryProductUrl) {
summaryProductUrl.textContent =
document.getElementById('productUrl')?.value || '-';
}
// Serial numbers
const serialNumbers = [];
document.querySelectorAll('input[name="serial_numbers[]"]').forEach(input => {
if (input && input.value && input.value.trim()) {
serialNumbers.push(input.value.trim());
}
});
const serialNumbersContainer = document.getElementById('summary-serial-numbers');
if (serialNumbersContainer) {
if (serialNumbers.length > 0) {
serialNumbersContainer.innerHTML = '
' +
serialNumbers.map(sn => `
${sn}
`).join('') +
'
';
} else {
serialNumbersContainer.textContent = 'None';
}
}
// Warranty details
const purchaseDate = document.getElementById('purchaseDate')?.value;
const summaryPurchaseDate = document.getElementById('summary-purchase-date');
if (summaryPurchaseDate) {
summaryPurchaseDate.textContent = purchaseDate ?
new Date(purchaseDate).toLocaleDateString() : '-';
}
const warrantyYears = document.getElementById('warrantyYears')?.value;
const summaryWarrantyYears = document.getElementById('summary-warranty-years');
if (summaryWarrantyYears) {
summaryWarrantyYears.textContent = warrantyYears ?
`${warrantyYears} ${warrantyYears > 1 ? 'years' : 'year'}` : '-';
}
// Calculate and display expiration date
const summaryExpirationDate = document.getElementById('summary-expiration-date');
if (summaryExpirationDate && purchaseDate && warrantyYears) {
const expirationDate = new Date(purchaseDate);
expirationDate.setFullYear(expirationDate.getFullYear() + parseInt(warrantyYears));
summaryExpirationDate.textContent = expirationDate.toLocaleDateString();
} else if (summaryExpirationDate) {
summaryExpirationDate.textContent = '-';
}
// Purchase price
const purchasePrice = document.getElementById('purchasePrice')?.value;
const summaryPurchasePrice = document.getElementById('summary-purchase-price');
if (summaryPurchasePrice) {
summaryPurchasePrice.textContent = purchasePrice ?
`$${parseFloat(purchasePrice).toFixed(2)}` : 'Not specified';
}
// Documents
const invoiceFile = document.getElementById('invoice')?.files[0];
const summaryInvoice = document.getElementById('summary-invoice');
if (summaryInvoice) {
summaryInvoice.textContent = invoiceFile ?
invoiceFile.name : 'No file selected';
}
const manualFile = document.getElementById('manual')?.files[0];
const summaryManual = document.getElementById('summary-manual');
if (summaryManual) {
summaryManual.textContent = manualFile ?
manualFile.name : 'No file selected';
}
// Tags
const summaryTags = document.getElementById('summary-tags');
if (summaryTags) {
if (selectedTags && selectedTags.length > 0) {
summaryTags.innerHTML = '';
selectedTags.forEach(tag => {
const tagElement = document.createElement('span');
tagElement.className = 'tag';
tagElement.style.backgroundColor = tag.color;
tagElement.style.color = getContrastColor(tag.color);
tagElement.textContent = tag.name;
summaryTags.appendChild(tagElement);
});
} else {
summaryTags.textContent = 'No tags 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 exportWarranties() {
// Get filtered warranties
let warrantiesToExport = [...warranties];
// Apply current filters
if (currentFilters.search) {
const searchTerm = currentFilters.search.toLowerCase();
warrantiesToExport = warrantiesToExport.filter(warranty => {
// Check if product name contains search term
const productNameMatch = warranty.product_name.toLowerCase().includes(searchTerm);
// Check if any tag name contains search term
const tagMatch = warranty.tags && Array.isArray(warranty.tags) &&
warranty.tags.some(tag => tag.name.toLowerCase().includes(searchTerm));
// Return true if either product name or tag name matches
return productNameMatch || tagMatch;
});
}
if (currentFilters.status !== 'all') {
warrantiesToExport = warrantiesToExport.filter(warranty =>
warranty.status === currentFilters.status
);
}
// Apply tag filter
if (currentFilters.tag !== 'all') {
const tagId = parseInt(currentFilters.tag);
warrantiesToExport = warrantiesToExport.filter(warranty =>
warranty.tags && Array.isArray(warranty.tags) &&
warranty.tags.some(tag => tag.id === tagId)
);
}
// 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,Tags\n";
// Add data rows
warrantiesToExport.forEach(warranty => {
// Format serial numbers as comma-separated string
const serialNumbers = Array.isArray(warranty.serial_numbers)
? warranty.serial_numbers.filter(s => s).join(', ')
: '';
// Format tags as comma-separated string
const tags = Array.isArray(warranty.tags)
? warranty.tags.map(tag => tag.name).join(', ')
: '';
// Format row data
const row = [
warranty.product_name || '',
formatDate(new Date(warranty.purchase_date)),
`${warranty.warranty_years || 0} ${warranty.warranty_years === 1 ? 'year' : 'years'}`,
formatDate(new Date(warranty.expiration_date)),
warranty.status || '',
serialNumbers,
tags
];
// Add row to CSV content
csvContent += row.map(field => `"${field.toString().replace(/"/g, '""')}"`).join(',') + '\n';
});
// Create a download link
const encodedUri = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('download', `warranties_export_${formatDate(new Date())}.csv`);
document.body.appendChild(link);
// Trigger download
link.click();
// Clean up
document.body.removeChild(link);
// Show success notification
showToast('Warranties exported successfully', 'success');
}
// Switch view of warranties list
function switchView(viewType) {
console.log(`Switching to ${viewType} view...`);
// Update currentView
currentView = viewType;
// Remove active class from all view buttons
gridViewBtn.classList.remove('active');
listViewBtn.classList.remove('active');
tableViewBtn.classList.remove('active');
// Update the view
warrantiesList.className = `warranties-list ${viewType}-view`;
// Hide table header if not in table view
if (tableViewHeader) {
tableViewHeader.style.display = viewType === 'table' ? 'flex' : 'none';
}
// Add active class to the selected view button
if (viewType === 'grid') {
gridViewBtn.classList.add('active');
} else if (viewType === 'list') {
listViewBtn.classList.add('active');
} else if (viewType === 'table') {
tableViewBtn.classList.add('active');
}
// Re-render warranties with the new view
console.log('Applying filters after switching view...');
applyFilters();
// Get prefix for user-specific preferences
const prefix = getPreferenceKeyPrefix();
// Save view preference to localStorage with the appropriate prefix
localStorage.setItem(`${prefix}warrantyView`, viewType);
localStorage.setItem('warrantyView', viewType); // Keep global setting for backward compatibility
}
// Load saved view preference
function loadViewPreference() {
// Get prefix for user-specific preferences
const prefix = getPreferenceKeyPrefix();
// First check for user-specific warrantyView
const userSavedView = localStorage.getItem(`${prefix}warrantyView`);
if (userSavedView) {
console.log(`Found user-specific view preference: ${userSavedView}`);
switchView(userSavedView);
return;
}
// If not found, check for user-specific defaultView
const userDefaultView = localStorage.getItem(`${prefix}defaultView`);
if (userDefaultView) {
console.log(`Found user-specific default view: ${userDefaultView}`);
switchView(userDefaultView);
return;
}
// If no user-specific preferences found, check global preferences for backward compatibility
const globalSavedView = localStorage.getItem('warrantyView');
if (globalSavedView) {
console.log(`Found global view preference: ${globalSavedView}`);
switchView(globalSavedView);
return;
}
const globalDefaultView = localStorage.getItem('defaultView');
if (globalDefaultView) {
console.log(`Found global default view: ${globalDefaultView}`);
switchView(globalDefaultView);
return;
}
// Default to grid view if no preferences found
console.log('No view preference found, defaulting to grid view');
switchView('grid');
}
// 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();
}
});
// Add a serial number input field
function addSerialNumberInput(container = serialNumbersContainer) {
if (!container) return;
// Create a new input group
const inputGroup = document.createElement('div');
inputGroup.className = 'serial-number-input';
// Create an input element
const input = document.createElement('input');
input.type = 'text';
input.className = 'form-control';
input.name = 'serial_numbers[]';
input.placeholder = 'Enter serial number';
// Check if this is the first serial number input
const isFirstInput = container.querySelectorAll('.serial-number-input').length === 0;
// Append input to the input group
inputGroup.appendChild(input);
// Only add remove button if this is not the first input
if (!isFirstInput) {
// Create a remove button
const removeButton = document.createElement('button');
removeButton.type = 'button';
removeButton.className = 'btn btn-sm btn-danger remove-serial';
removeButton.innerHTML = '';
// Add event listener to remove button
removeButton.addEventListener('click', function() {
container.removeChild(inputGroup);
});
// Append remove button to the input group
inputGroup.appendChild(removeButton);
}
// Insert the new input group before the add button
const addButton = container.querySelector('.add-serial');
if (addButton) {
container.insertBefore(inputGroup, addButton);
} else {
container.appendChild(inputGroup);
// Create and append an add button if it doesn't exist
const addButton = document.createElement('button');
addButton.type = 'button';
addButton.className = 'btn btn-sm btn-secondary add-serial';
addButton.innerHTML = ' Add Serial Number';
addButton.addEventListener('click', function() {
addSerialNumberInput(container);
});
container.appendChild(addButton);
}
}
// 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);
}
// Update file name display when a file is selected
function updateFileName(event, inputId = 'invoice', outputId = 'fileName') {
const input = event ? event.target : document.getElementById(inputId);
const output = document.getElementById(outputId);
if (!input || !output) return;
if (input.files && input.files[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 < expiringSoonDays) {
console.log(`Using expiringSoonDays: ${expiringSoonDays} for warranty: ${processedWarranty.product_name}`);
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();
// Get expiring soon days from user preferences if available
try {
const prefsResponse = await fetch('/api/auth/preferences', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (prefsResponse.ok) {
const data = await prefsResponse.json();
if (data && data.expiring_soon_days) {
const oldValue = expiringSoonDays;
expiringSoonDays = data.expiring_soon_days;
console.log('Updated expiring soon days from preferences:', expiringSoonDays);
// If we already have warranties loaded and the value changed, reprocess them
if (warranties && warranties.length > 0 && oldValue !== expiringSoonDays) {
console.log('Reprocessing warranties with new expiringSoonDays value');
warranties = warranties.map(warranty => processWarrantyData(warranty));
renderWarrantiesTable(warranties);
}
}
}
} catch (error) {
console.error('Error loading preferences:', error);
// Continue with default value
}
// 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');
// Populate tag filter dropdown with tags from warranties
populateTagFilter();
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.sortBy) {
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';
// Debug file paths
console.log(`Warranty ID ${warranty.id} - Product: ${warranty.product_name}`);
console.log(`- Invoice path: ${warranty.invoice_path}`);
console.log(`- Manual path: ${warranty.manual_path}`);
// 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() !== '')
: [];
// Prepare tags HTML
const tagsHtml = warranty.tags && warranty.tags.length > 0
? `