// CSV Bulk Import JavaScript // Handles file upload with AJAX, progress indicators, and result display document.addEventListener('DOMContentLoaded', function () { setupCsvImportForm('csvImportCustomersForm', 'csvCustomersFile', 'csvImportCustomersBtn', '/Tools/CsvImportCustomers', 'csvCustomersResults'); setupCsvImportForm('csvImportCatalogForm', 'csvCatalogFile', 'csvImportCatalogBtn', '/Tools/CsvImportCatalogItems', 'csvCatalogResults'); setupCsvImportForm('csvImportInventoryForm', 'csvInventoryFile', 'csvImportInventoryBtn', '/Tools/CsvImportInventoryItems', 'csvInventoryResults'); setupCsvImportForm('csvImportQuotesForm', 'csvQuotesFile', 'csvImportQuotesBtn', '/Tools/CsvImportQuotes', 'csvQuotesResults'); setupCsvImportForm('csvImportJobsForm', 'csvJobsFile', 'csvImportJobsBtn', '/Tools/CsvImportJobs', 'csvJobsResults'); setupCsvImportForm('csvImportAppointmentsForm', 'csvAppointmentsFile', 'csvImportAppointmentsBtn', '/Tools/CsvImportAppointments', 'csvAppointmentsResults'); setupCsvImportForm('csvImportEquipmentForm', 'csvEquipmentFile', 'csvImportEquipmentBtn', '/Tools/CsvImportEquipment', 'csvEquipmentResults'); setupCsvImportForm('csvImportMaintenanceForm', 'csvMaintenanceFile', 'csvImportMaintenanceBtn', '/Tools/CsvImportMaintenance', 'csvMaintenanceResults'); setupCsvImportForm('csvImportSettingsForm', 'csvSettingsFile', 'csvImportSettingsBtn', '/Tools/CsvImportCompanySettings', 'csvSettingsResults'); setupCsvImportForm('csvImportVendorsForm', 'csvVendorsFile', 'csvImportVendorsBtn', '/Tools/CsvImportVendors', 'csvVendorsResults'); setupCsvImportForm('csvImportShopWorkersForm', 'csvShopWorkersFile', 'csvImportShopWorkersBtn', '/Tools/CsvImportShopWorkers', 'csvShopWorkersResults'); setupCsvImportForm('csvImportPrepServicesForm', 'csvPrepServicesFile', 'csvImportPrepServicesBtn', '/Tools/CsvImportPrepServices', 'csvPrepServicesResults'); }); function setupCsvImportForm(formId, fileInputId, submitBtnId, actionUrl, resultsId) { const form = document.getElementById(formId); if (!form) return; form.addEventListener('submit', async function (e) { e.preventDefault(); const fileInput = document.getElementById(fileInputId); const submitBtn = document.getElementById(submitBtnId); const resultsDiv = document.getElementById(resultsId); const spinner = submitBtn.querySelector('.spinner-border'); // Validate file selection if (!fileInput.files || fileInput.files.length === 0) { showToast('Please select a CSV file to import.', 'error'); return; } const file = fileInput.files[0]; // Validate file extension if (!file.name.toLowerCase().endsWith('.csv')) { showToast('Please select a valid CSV file.', 'error'); return; } // Validate file size (max 10MB) const maxSize = 10 * 1024 * 1024; // 10MB if (file.size > maxSize) { showToast('File size must be less than 10MB.', 'error'); return; } // Show loading state spinner.classList.remove('d-none'); submitBtn.disabled = true; resultsDiv.classList.add('d-none'); resultsDiv.innerHTML = ''; // Prepare form data (use the form element so all fields, including account selects, are included) const formData = new FormData(form); // Get anti-forgery token const token = document.querySelector('input[name="__RequestVerificationToken"]').value; try { const response = await fetch(actionUrl, { method: 'POST', body: formData, headers: { 'RequestVerificationToken': token } }); const result = await response.json(); // Display results displayImportResults(result, resultsDiv); // Show toast notification if (result.success) { showToast(`Import completed: ${result.successCount} records imported successfully!`, 'success'); // Clear file input fileInput.value = ''; } else { showToast('Import completed with errors. Please review the details below.', 'error'); } } catch (error) { console.error('Import error:', error); showToast('An error occurred during import: ' + error.message, 'error'); displayImportResults({ success: false, message: error.message, errors: [error.toString()] }, resultsDiv); } finally { // Hide loading state spinner.classList.add('d-none'); submitBtn.disabled = false; } }); } function displayImportResults(result, resultsDiv) { resultsDiv.classList.remove('d-none'); const cardClass = result.success ? 'border-success' : 'border-danger'; const headerClass = result.success ? 'bg-success' : 'bg-danger'; const icon = result.success ? 'check-circle' : 'exclamation-triangle'; const skipped = result.skippedCount || 0; const colSize = skipped > 0 ? 'col-md-3' : 'col-md-4'; let html = `
Import Results
${result.successCount || 0}
Imported
${skipped > 0 ? `
${skipped}
Skipped (already exist)
` : ''}
${result.errorCount || 0}
Errors
${result.totalRows || 0}
Total Rows

${result.message || 'Import completed.'}

`; // Display errors if (result.errors && result.errors.length > 0) { html += `
Errors:
    `; result.errors.forEach(error => { html += `
  • ${escapeHtml(error)}
  • `; }); html += `
`; } // Display warnings if (result.warnings && result.warnings.length > 0) { html += `
Warnings:
    `; result.warnings.forEach(warning => { html += `
  • ${escapeHtml(warning)}
  • `; }); html += `
`; } html += `
`; resultsDiv.innerHTML = html; } function showToast(message, type = 'success') { const toastId = type === 'success' ? 'successToast' : 'errorToast'; const toastElement = document.getElementById(toastId); if (!toastElement) return; const toastBody = toastElement.querySelector('.toast-body'); toastBody.textContent = message; const toast = new bootstrap.Toast(toastElement, { autohide: true, delay: 5000 }); toast.show(); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }